﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

namespace SW2A311_Lib
{
    #region Enums
    public enum ObjectType { Project, CreateUser, Chat, LogInUser };
    #endregion

    #region Klasser
    /// <summary>
    /// Klasse med metoderne til at skrive objekter til en fil.
    /// </summary>
    /// <remarks>
    /// Serialisering er en indbygget funktionalitet i C\#. Biblioteket ligger i "System.Runtime.Serialization" og fungere ved at man omdanner 
    /// et object til en strøm af bytes der efterfølgende kan skrives til en fil, memory eller over netværket. Når objektet serialiseres gemmes 
    /// nogle data om objektet som dets type, asembly navn, version og de specifikke fields fra objektet man har valgt at serialisere. Hvilke fields 
    /// der bliver gemt i byte-strømmen styres i de enkelte klasser og bliver derfor uddybet i dem.
    /// </remarks>
    public class ObjectToFile
    {
        /// <summary>
        /// BinaryFormatter instantieres. 
        /// </summary>
        private BinaryFormatter bFormatter = new BinaryFormatter();

        public ObjectToFile()
        {
        }

        /// <summary>
        /// Metoden tager et filnavn og objektet der skal gemmes og skriver det i en fil med det givne navn.
        /// </summary>
        /// <remarks>
        /// Den tager alle slags objekter (T) hvis bare objektet er forberedt til serialisering.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="filename"></param>
        /// <param name="objectToSerialize"></param>
        public void SerializeObject<T>(string filename, T objectToSerialize)
        {
            Stream stream = File.Open(filename, FileMode.Create); // Der instantieres/forberedes et stream til en fil med valgte navn.
            bFormatter.Serialize(stream, objectToSerialize);      // BinaryFormatter serialisere ObjectToSerialize og sender resultatet til vores stream der skriver det til filen.
            stream.Close();
        }
        /// <summary>
        /// Læser fra filen med det givne filnavn og returnerer et objekt med af den givne type.
        /// </summary>
        /// <remarks>
        /// Den tager alle slags objekter (T) hvis bare objektet er forberedt til serialisering.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="filename"></param>
        /// <returns></returns>
        public T DeSerializeObject<T>(string filename)
        {
                T objectToDeserialize;                                // Der genereres et tomt object svarende til det vi vil hente fra filen.
                Stream stream = File.Open(filename, FileMode.Open);   // Et stream åbnes fra filen.
                objectToDeserialize = (T)bFormatter.Deserialize(stream); // Vores stream deserialiseres og dataen indsættes i det tomme objekt.
                stream.Close();
                return objectToDeserialize;                           // Objektet returneres.
        }
    }
    /// <summary>
    /// Klasse med metoderne til at skrive objekter til et byte array.
    /// </summary>
    /// <remarks>
    /// Serialisering er en indbygget funktionalitet i C\#. Biblioteket ligger i "System.Runtime.Serialization" og fungere ved at man omdanner 
    /// et object til en strøm af bytes der efterfølgende kan skrives til en fil, memory eller over netværket. Når objektet serialiseres gemmes 
    /// nogle data om objektet som dets type, asembly navn, version og de specifikke fields fra objektet man har valgt at serialisere. Hvilke fields 
    /// der bliver gemt i byte-strømmen styres i de enkelte klasser og bliver derfor uddybet i dem.
    /// </remarks>
    public class ObjectToByteArray
    {
        /// <summary>
        /// BinaryFormatter instantieres. 
        /// </summary>
        private BinaryFormatter bFormatter = new BinaryFormatter();

        public ObjectToByteArray()
        {
        }
        /// <summary>
        /// Metoden tager et objekt og serializerer det, hvorefter det returneres i et byte-array.
        /// </summary>
        /// <remarks>
        /// Den tager alle slags objekter (T) hvis bare objektet er forberedt til serialisering.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="objectToSerialize"></param>
        /// <returns></returns>
        public byte[] SerializeObject<T>(T objectToSerialize)
        {
            byte[] byteArray;                                    // Tomt byte array instantieres så det kan modtage den serialiserede data.
            MemoryStream stream = new MemoryStream();            // Der instantieres et stream i hukommelsen.
            bFormatter.Serialize(stream, objectToSerialize);     // objectToSerialize serialiseres med BinaryFormatter og sendes til vores stream.
            byteArray = stream.ToArray();                        // Streamet med dataen skrives i det tomme byte array.
            stream.Close();
            return byteArray;                                    // Arrayet med dataen returneres.
        }
        /// <summary>
        /// Metoden skal have en returtype og et byte array og deserialiserer derefter byte array'et og returnerer det som returtypen.
        /// </summary>
        /// <remarks>
        /// Den tager alle slags objekter (T) hvis bare objektet er forberedt til serialisering.
        /// </remarks>
        /// <typeparam name="T"></typeparam>
        /// <param name="byteArray"></param>
        /// <returns></returns>
        public T DeSerializeObject<T>(byte[] byteArray)
        {
            T objectToSerialize;                                  // Objekt instantieres.
            MemoryStream stream = new MemoryStream(byteArray);    // Stream instatieres i hukommelsen.
            objectToSerialize = (T)bFormatter.Deserialize(stream);// Streamet deserialiseres og dataen lægges i det tomme objekt. 
            stream.Close();
            return objectToSerialize;                             // Objektet returneres.   
        }
    }

    /// <summary>
    /// EncapsulationClass bruges til at indkapsle de objekter vi sender over netværket så vi nemt kan deserialize dem igen og
    /// ud fra TypeCode vide hvordan vi skal videre behandle objektet hos modtageren.
    /// </summary>
    [Serializable()]
    public class EncapsulationClass : ISerializable
    {
        public byte[] SentObject { get; private set; }          // Objektet der skal sendes.
        public ObjectType TypeCode { get; private set; }        // Objektets type gemmes i en type vi har defineret som ObjectType.
        /// <summary>
        /// Constructoren tager det object der skal indkapsles som input og gemmer det i SentObject field.
        /// </summary>
        /// <remarks>
        /// Kalder GetObjectType metoden så TypeCode også får en værdi.
        /// </remarks>
        /// <param name="objectIn"></param>
        public EncapsulationClass(object objectIn)
        {
            ObjectToByteArray serializer = new ObjectToByteArray();     // ObjectToByteArray instantieres. 
            GetObjectType(objectIn);                                    // Vi finder og gemmer objektets type med GetObjectType metoden.
            SentObject = serializer.SerializeObject<object>(objectIn);  // Objektet serialiseres og gemmes.
        }
        /// <summary>
        /// Construktor til når objektet serialiseres. Sørger for både SentObject og TypeCode bliver serializeret.
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public EncapsulationClass(SerializationInfo info, StreamingContext ctxt)
        {
            this.SentObject = (byte[])info.GetValue("SentObject", typeof(byte[]));
            this.TypeCode = (ObjectType)info.GetValue("TypeCode", typeof(ObjectType));
        }
        /// <summary>
        /// Metode til deserializering.
        /// </summary>
        /// <param name="info"></param>
        /// <param name="ctxt"></param>
        public virtual void GetObjectData(SerializationInfo info, StreamingContext ctxt)
        {
            info.AddValue("SentObject", this.SentObject);
            info.AddValue("TypeCode", this.TypeCode);
        }
        /// <summary>
        /// Skifter TypeCode for et User object fra LogInUser til CreateUser.
        /// </summary>
        public void SetAsCreateUser()
        {
            if (TypeCode == ObjectType.LogInUser)
            {
                TypeCode = ObjectType.CreateUser;
            }
            else
                throw new ArgumentException("Object is not a User!");
        }
        /// <summary>
        /// Finder en TypeCode ud fra objektets .GetType og gemmer det i TypeCode field.
        /// </summary>
        /// <param name="objectIn"></param>
        public void GetObjectType(object objectIn)
        {
            switch (objectIn.GetType().ToString())
            {
                case "SW2A311_Lib.ProjectData":
                    TypeCode = ObjectType.Project;
                    break;
                case "SW2A311_Lib.ChatMessageEncapsulation":
                    TypeCode = ObjectType.Chat;
                    break;
                case "SW2A311_Lib.User":
                    TypeCode = ObjectType.LogInUser;
                    break;
                default: break;
            }
        }

    }
    #endregion
}